package com.zj.ckproxy.config;

import com.zj.baseproxy.config.XmlConfig;
import com.zj.baseproxy.interfaces.BeanCreateRule;
import com.zj.ckproxy.handler.CkHandler;
import com.zj.ckproxy.handler.CkInvocationHandler;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.NamingStrategy;
import net.bytebuddy.description.type.TypeDescription;
import net.bytebuddy.dynamic.DynamicType;
import net.bytebuddy.implementation.InvocationHandlerAdapter;
import net.bytebuddy.matcher.ElementMatchers;
import org.apache.commons.lang3.StringUtils;
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;

import java.io.IOException;
import java.lang.reflect.InvocationHandler;
import java.util.Objects;

/**
 * @author arthur_zhou
 * @date 2023/10/8 11:35
 */
@Slf4j
@Configuration
@RequiredArgsConstructor
@EnableConfigurationProperties(CkProperties.class)
public class CkConfig implements XmlConfig {

    private final CkProperties ckProperties;

    @Bean("ckBeanCreateRule")
    @ConditionalOnMissingBean(name = "ckBeanCreateRule")
    public BeanCreateRule beanCreateRule(@Qualifier("ckInvocationHandler") InvocationHandler invocationHandler) {
        return new BeanCreateRule() {
            @Override
            public <T> T create(Class<T> clazz) {
                // 使用byte-buddy动态代理接口
                Object o = null;
                try {
                    DynamicType.Builder.MethodDefinition.ReceiverTypeDefinition<Object> receiverTypeDefinition = new ByteBuddy().with(new NamingStrategy.AbstractBase() {
                        @Override
                        protected String name(TypeDescription typeDescription) {
                            return "com.zj.ckproxy.proxy." + clazz.getSimpleName() + "Impl";
                        }
                    })
                            .subclass(Object.class)
                            .implement(clazz)
                            // 拦截除了Object的方法，也就是所有未实现的方法
                            .method(ElementMatchers.not(ElementMatchers.isDeclaredBy(Object.class)))
                            .intercept(InvocationHandlerAdapter.of(invocationHandler));
                    o = receiverTypeDefinition.make()
                            .load(clazz.getClassLoader())
                            .getLoaded()
                            .newInstance();
                } catch (InstantiationException | IllegalAccessException e) {
                    log.warn("ck bean生成失败", e);
                }
                return clazz.cast(o);
            }
        };
    }

    /**
     * @return 读取xml文件信息
     * @throws IOException IOException
     */
    @Override
    @Bean("ckConfiguration")
    @ConditionalOnMissingBean(name = "ckConfiguration")
    public org.apache.ibatis.session.Configuration mybatisConfiguration() throws IOException {
        org.apache.ibatis.session.Configuration targetConfiguration = new org.apache.ibatis.session.Configuration();
        String[] mapperLocations = ckProperties.getMapperLocations();

        if (Objects.isNull(mapperLocations) || mapperLocations.length == 0) {
            log.warn("未找到xml文件");
            return targetConfiguration;
        }
        for (String mapperLocation : mapperLocations) {
            Resource[] resources = new PathMatchingResourcePatternResolver().getResources(mapperLocation);
            for (Resource resource : resources) {
                XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(resource.getInputStream(),
                        targetConfiguration, resource.toString(), targetConfiguration.getSqlFragments());
                xmlMapperBuilder.parse();
            }
        }
        return targetConfiguration;
    }

    /**
     * @param configuration xml信息
     * @return mapper方法拦截器
     */
    @Override
    @Bean("ckInvocationHandler")
    @ConditionalOnMissingBean(name = "ckInvocationHandler")
    public InvocationHandler invocationHandler(org.apache.ibatis.session.Configuration configuration) {
        String url = ckProperties.getUrl();
        if (StringUtils.isEmpty(url)) {
            log.warn("ck路径未配置");
        }
        return new CkInvocationHandler(url, configuration);
    }
    @Bean
    @ConditionalOnMissingBean(name = "ckHandler")
    public CkHandler ckHandler() {
        return new CkHandler();
    }

}
